package org.nimi317.web_gis.controller;

import cn.dev33.satoken.annotation.SaCheckRole;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.nimi317.web_gis.Enum.JobEnum;
import org.nimi317.web_gis.annotation.JobClass;
import org.nimi317.web_gis.annotation.JobMethod;
import org.nimi317.web_gis.exception.E;
import org.nimi317.web_gis.exception.RException;
import org.nimi317.web_gis.form.post.JobPost;
import org.nimi317.web_gis.form.put.JobPut;
import org.nimi317.web_gis.entity.Job;
import org.nimi317.web_gis.entity.R;
import org.nimi317.web_gis.service.IJobService;
import org.nimi317.web_gis.task.ScheduledTask;
import org.springframework.beans.BeanUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.support.CronExpression;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * @author thunderobot
 */
@RestController
@RequestMapping("jobs")
@Slf4j
@SaCheckRole("admin")
public class JobController {

    private final List<Map<String, ?>> jobs = new ArrayList<>();

    private final IJobService jobService;

    private final ScheduledTask scheduledTask;

    public JobController(ApplicationContext context, IJobService jobService, ScheduledTask scheduledTask) {
        this.jobService = jobService;
        this.scheduledTask = scheduledTask;
        Map<String, Object> beans = context.getBeansWithAnnotation(JobClass.class);
        for (Map.Entry<String, Object> entry : beans.entrySet()) {
            Method[] methods = entry.getValue().getClass().getDeclaredMethods();
            List<String> strings = Arrays.stream(methods).filter(m -> m.isAnnotationPresent(JobMethod.class)).map(Method::getName).toList();
            jobs.add(Map.of("bean", entry.getKey(), "method", strings));
        }
    }

    /**
     * 获取可选任务列表
     */
    @GetMapping("list")
    public R<?> getJobList() {
        return R.ok(jobs);
    }

    /**
     * 获取当前任务列表
     */
    @GetMapping
    public R<?> getJob() {
        return R.ok(jobService.list());
    }

    /**
     * 更改时间
     */
    @PutMapping
    public R<?> change(@RequestBody @Validated JobPut put) {
        if (!CronExpression.isValidExpression(put.getCron())) {
            throw new RException(E.CronException);
        }
        Job job = new Job();
        BeanUtils.copyProperties(put, job);
        jobService.updateById(job);
        return R.ok();
    }

    /**
     * 启动任务
     */
    @GetMapping("start/{id}")
    public R<?> start(@PathVariable Integer id) {
        jobService.start(id);
        scheduledTask.start(id);
        return R.ok("任务已启动");
    }

    /**
     * 停止任务
     */
    @GetMapping("stop/{id}")
    public R<?> stop(@PathVariable Integer id) {
        jobService.stop(id);
        scheduledTask.stop(id);
        return R.ok("任务已停止");
    }

    /**
     * 新增任务
     */
    @PostMapping
    public R<?> add(@RequestBody @Validated JobPost post) {
        if (!CronExpression.isValidExpression(post.getCron())) {
            throw new RException(E.CronException);
        }
        List<Map<String, ?>> bean = jobs.stream().filter(m -> m.get("bean").equals(post.getBean())).toList();
        if (ObjectUtils.isNotEmpty(bean)) {
            List<String> method = (List<String>) bean.get(0).get("method");
            if (method != null && method.contains(post.getMethod())) {
                Job job = new Job();
                job.setStatus(JobEnum.Stopped);
                BeanUtils.copyProperties(post, job);
                jobService.save(job);
                return R.ok("任务已成功添加");
            }
        }
        throw new RException(E.JobNotFoundException);
    }

    @DeleteMapping("{id}")
    public R<?> delete(@PathVariable Integer id) {
        jobService.removeById(id);
        scheduledTask.remove(id);
        return R.ok("任务已成功删除");
    }

    @GetMapping("dict")
    public R<?> dict() {
        return R.ok(Arrays.stream(JobEnum.values()).map(JobEnum::getObject).toList());
    }

    @GetMapping("cron")
    public R<?> cron(@RequestParam String cron) {
        boolean validExpression = CronExpression.isValidExpression(cron);
        if (!validExpression) {
            throw new RException(E.CronException);
        }
        return R.ok();
    }
}
